package org.example.service.impl;

import cn.hutool.core.codec.Base64Decoder;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.WebSocket;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.example.commonapi.domain.FormData;
import org.example.commonapi.domain.Practice;
import org.example.commonapi.domain.PracticeHistory;
import org.example.commonapi.domain.R;
import org.example.commonapi.domain.ToolFrequency;
import org.example.commonapi.domain.User;
import org.example.commonapi.dto.SpeechAssessmentDTO;
import org.example.commonapi.mapper.PracticeHistoryMapper;
import org.example.commonapi.mapper.PracticeMapper;
import org.example.commonapi.mapper.ToolFrequencyMapper;
import org.example.commonapi.mapper.UserMapper;
import org.example.commonapi.request.ParamBuilder;
import org.example.commonapi.response.RoleContent;
import org.example.commonapi.response.bigmodel.SparkChatListener;
import org.example.commonapi.response.characterRecognition.CharacterRecognitionResponse;
import org.example.commonapi.response.correctError.CorrectErrorResponse;
import org.example.commonapi.response.faceCompare.FaceCompareResponse;
import org.example.commonapi.response.faceCompare.Text;
import org.example.commonapi.response.formulaRecognition.FormulaRecognitionResponse;
import org.example.commonapi.response.machineTranslate.MachineTranslateResponse;
import org.example.commonapi.response.pictureDocument.PictureDocumentListener;
import org.example.commonapi.response.speechAssessment.SpeechAssessmentListener;
import org.example.commonapi.response.textRewrite.TextRewriteResponse;
import org.example.commonapi.util.JwtUtil;
import org.example.commonapi.util.oss.OssService;
import org.example.commonapi.util.redis.AiPrefix;
import org.example.commonapi.util.redis.RedisService;
import org.example.commonapi.vo.CorrectionErrorVO;
import org.example.commonapi.vo.PhotoCalculateVO;
import org.example.commonapi.vo.WordScoreVO;
import org.example.service.AiToolService;
import org.example.utils.AuthUtil;
import org.example.utils.DraftWithOrigin;
import org.example.utils.FileUtil;
import org.example.utils.HttpUtil;
import org.example.utils.Mp4ExtractAudioUtils;
import org.example.utils.MyWebSocketClient;
import org.java_websocket.client.WebSocketClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;

import static org.example.utils.AuthUtil.buildHttpHeader;
import static org.example.utils.AuthUtil.getAuthUrl;
import static org.example.utils.AuthUtil.getHandShakeParams;

@Service
public class AiToolServiceImpl implements AiToolService {
    @Value("${APP.APPID}")
    public String appId;
    @Value("${APP.APISecret}")
    public String apiSecret;
    @Value("${APP.APIKey}")
    public String apiKey;
    @Value("${APP.RealTimeASRSecretKey}")
    public String realTimeSecretKey;
    //JSON解析工具
    private static final Gson gson = new Gson();
    @Autowired
    private RedisService redisService;
    @Resource
    private JwtUtil jwtUtil;
    @Autowired
    private ToolFrequencyMapper toolFrequencyMapper;
    @Autowired
    private OssService ossService;
    @Autowired
    private PracticeMapper practiceMapper;
    @Autowired
    private PracticeHistoryMapper practiceHistoryMapper;
    @Autowired
    private UserMapper userMapper;

    // 实时语音识别是否停止
    private boolean realTimeASRFlag = false;

    @Override
    public R<String> machineTranslate(String text, String from, String to) {
        String hostUrl = "https://itrans.xf-yun.com/v1/its";
        String params = buildMachineTranslateParams(appId,"",text,from,to);
        try {
            String resp = doRequest(hostUrl,params,apiKey,apiSecret);
            System.out.println("resp=>" + resp);
            MachineTranslateResponse machineTranslateResponse = gson.fromJson(resp, MachineTranslateResponse.class);
            String textBase64Decode=new String(Base64.getDecoder().decode(machineTranslateResponse.getPayload().getResult().getText()), "UTF-8");
            JSONObject jsonObject = JSON.parseObject(textBase64Decode);
            System.out.println("text字段Base64解码后=>"+jsonObject);
            return R.success(jsonObject.getJSONObject("trans_result").get("dst").toString());
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public R<String> aiCheckVideo(String videoUrl) throws Exception {
        // 审查视频链接
        String hostUrl = "https://audit.iflyaisol.com/audit/v2/video";
        // 查询视频审查进度链接
        String queryUrl = "https://audit.iflyaisol.com/audit/v2/query";
        // 1、业务参数 notify_url 回调接口 当审查完成之后回调的接口
        String json = "{\n" +
                "  \"video_list\": [\n" +
                "    {\n" +
                "      \"video_type\": \"" + videoUrl.substring(videoUrl.length() - 3) + "\",\n" +
                "      \"file_url\": \"" + videoUrl + "\",\n" +
                "      \"name\": \"" + "xxx" + videoUrl.substring(videoUrl.length() - 8) + "\"\n" +
                "    }\n" +
                "  ],\n" +
                "  \"notify_url\": \"\"\n" +
                "}";
        // 2、获取鉴权
        Map<String, String> urlParams = AuthUtil.getAuthParams(appId, apiKey, apiSecret);
        ///3、发起请求
        String returnResult = HttpUtil.doPostJsonMapParams(hostUrl, urlParams, json);
        System.out.println("请到回调地址或存储库查看结果，调用信息如下：\n" + returnResult);
        String request_id = (String) JSONObject.parseObject(JSONObject.parseObject(returnResult).get("data").toString()).get("request_id");
        //4、拿到request_id后主动查询合规结果
        while (true) {
            String queryJson = "{\n" +
                    "  \"request_id\": \"" + request_id + "\"\n" +
                    "}";
            urlParams = AuthUtil.getAuthParams(appId, apiKey, apiSecret);
            returnResult = HttpUtil.doPostJsonMapParams(queryUrl, urlParams, queryJson);
            Integer finishIndex = (Integer) JSONObject.parseObject(JSONObject.parseObject(returnResult).get("data").toString()).get("audit_status");
            if (finishIndex == 0) {
                System.out.println("视频合规待审核...");
            }
            if (finishIndex == 1) {
                System.out.println("视频合规审核中...");
            }
            if (finishIndex == 2) {
                System.out.println("视频合规审核完成：");
                System.out.println(returnResult);
                // 转化为json对象
                JSONObject jsonObject = JSONObject.parseObject(returnResult);
                JSONObject data = jsonObject.getJSONObject("data");
                JSONArray resultList = data.getJSONArray("result_list");
                String suggest = resultList.getJSONObject(0).get("suggest").toString();
                return R.success(suggest);
            }
            if (finishIndex == 4) {
                System.out.println("视频合规审核异常：");
                System.out.println(returnResult);
            }
            Thread.sleep(5000);
        }
    }

    @Override
    public R<List<CorrectionErrorVO>> aiCorrectError(String text) throws Exception {
        String hostUrl = "https://api.xf-yun.com/v1/private/s9a87e3ec";
        String authUrl = AuthUtil.getAuthUrl(hostUrl,apiKey,apiSecret,"POST");
        String requestJSON = getCorrectErrorRequest(text);
        String result = HttpUtil.doPostJsonStringParams(authUrl,requestJSON);
        System.out.println("文本纠错结果=>" + result);
        CorrectErrorResponse correctErrorResponse = gson.fromJson(result, CorrectErrorResponse.class);
        // 对text字段使用64解码器解码后
        String base64DecodeResult = new String(Base64.getDecoder().decode(correctErrorResponse.getPayload().getResult().getText()),StandardCharsets.UTF_8);
        // 转化为JSON对象
        JSONObject jsonObject = JSONObject.parseObject(base64DecodeResult);
        String[] errorType = {"punc","leader","org","pol","grammar_pc","order","idm","word","char","redund","miss","dapei","number","addr","name"};
        List<CorrectionErrorVO> correctErrorVOList = new ArrayList<>();
        // 英文转化为对应的中文
        Map<String,String> EN2CH = new HashMap<>();
        EN2CH.put("punc","标点纠错");
        EN2CH.put("pol","政治术语纠错");
        EN2CH.put("char","别字纠错");
        EN2CH.put("word","别词纠错");
        EN2CH.put("redund","语法纠错-冗余");
        EN2CH.put("miss","语法纠错-缺失");
        EN2CH.put("order","语法纠错-乱序");
        EN2CH.put("dapei","搭配纠错");
        EN2CH.put("idm","成语纠错");
        EN2CH.put("org","机构名纠错");
        EN2CH.put("leader","领导人职称纠错");
        EN2CH.put("number","数字纠错");
        EN2CH.put("addr","地名纠错");
        EN2CH.put("name","全文人名纠错");
        EN2CH.put("grammar_pc","句式杂糅&语义重复");
        for (String type : errorType) {
            // 获取jsonArray
            JSONArray jsonArray = jsonObject.getJSONArray(type);
            if (jsonArray != null && !jsonArray.isEmpty()) {
                // 将jsonArray转化为字符串List
                List<String> errorDetailList = new ArrayList<>();
                for (Object o : jsonArray) {
                    // 去掉开头和结尾的字符
                    String errorDetail = o.toString();
                    errorDetail = errorDetail.substring(1, errorDetail.length() - 1);
                    errorDetailList.add(errorDetail);
                }
                // 构造返回给前端的VO
                CorrectionErrorVO correctionErrorVO = new CorrectionErrorVO();
                correctionErrorVO.setType(EN2CH.get(type));
                correctionErrorVO.setErrorDetails(errorDetailList);
                // 加入到最后返回给前端的结果中
                correctErrorVOList.add(correctionErrorVO);
            }
        }
        System.out.println(correctErrorVOList);
        return R.success(correctErrorVOList);
    }

    @Override
    public R<String> aiTextRewrite(String text, String level) throws Exception {
        // 使用另一个账号的api接口
        String appId = "4594fbd8";
        String apiSecret = "ZTZmMTYxZWM2MDI5MGM5MmQzMDA2Mjkw";
        String apiKey = "e1587f75df5bc33d510d10f21b45fdb9";
        String hostUrl = "https://api.xf-yun.com/v1/private/se3acbe7f";
        String params = buildTextRewriteRequest(appId,level,text);
        String resp = doRequest(hostUrl,params,apiKey,apiSecret);
        TextRewriteResponse response = JSON.parseObject(resp,TextRewriteResponse.class);
        String textBase64Decode=new String(Base64.getDecoder().decode(response.getPayload().getResult().getText()), StandardCharsets.UTF_8);
        // 返回改写后的文本，和改写文本对应的位置信息
        System.out.println("text字段Base64解码后=>"+textBase64Decode);
        // 根据[[对结果进行分割
        String[] textSplit = textBase64Decode.split("\\[\\[");
        return R.success(textSplit[1].substring(1,textSplit[1].length()-3));
    }

    @Override
    public R<String> characterRecognition(String UID) throws Exception {
        String hostUrl = "https://api.xf-yun.com/v1/private/sf8e6aca1";
        String base64Link = redisService.get(AiPrefix.BASE64URL,UID);
        String params = buildCharacterRecognitionRequest(base64Link);
        String resp = doRequest(hostUrl,params,apiKey,apiSecret);
        CharacterRecognitionResponse response = JSON.parseObject(resp, CharacterRecognitionResponse.class);
        String textBase64Decode = new String(Base64.getDecoder().decode(response.getPayload().getResult().getText()), StandardCharsets.UTF_8);
        JSONObject jsonObject = JSON.parseObject(textBase64Decode);
        System.out.println("textbase64解码=>" + jsonObject);
        JSONArray pages = jsonObject.getJSONArray("pages");
        JSONArray lines = pages.getJSONObject(0).getJSONArray("lines");
        StringBuilder content = new StringBuilder();
        for (int i=0;i<lines.size();i++){
            JSONArray words = lines.getJSONObject(i).getJSONArray("words");
            content.append(words.getJSONObject(0).getString("content"));
        }
        return R.success(content.toString());
    }

    @Override
    public R<String> pictureDocument(String uid, String type,String encoding) throws Exception {
        String hostUrl = "https://ws-api.xf-yun.com/v1/private/ma008db16";
        String authUrl = AuthUtil.getAuthUrl(hostUrl,apiKey,apiSecret,"GET");
        String base64Link = redisService.get(AiPrefix.BASE64URL,uid);
        OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request.Builder().url(authUrl).build();
        String params = buildPictureDocumentRequest(base64Link,type,encoding);
        PictureDocumentListener listener = new PictureDocumentListener(type,ossService);
        WebSocket webSocket = client.newWebSocket(request, listener);
        webSocket.send(params);
        while ( !listener.getWsCloseFlag() ) {
            Thread.sleep(100);
        }
        return R.success(listener.getDownloadLink());
    }

    @Override
    public R<String> formulaRecognition(String uid) throws Exception {
        String hostUrl = "https://rest-api.xfyun.cn/v2/itr";
        String authUrl = AuthUtil.getAuthUrl(hostUrl,apiKey,apiSecret,"POST");
        String base64Link = redisService.get(AiPrefix.BASE64URL,uid);
        String requestJSON = buildFormulaRecognitionRequest(base64Link);
        String result = HttpUtil.doPostJsonStringParams(authUrl, requestJSON);
        FormulaRecognitionResponse response = gson.fromJson(result, FormulaRecognitionResponse.class);
        // 获取识别结果
        JSONObject data = JSONObject.parseObject(response.getData().toString());
        JSONArray region = data.getJSONArray("region");
        JSONObject recog = region.getJSONObject(0).getJSONObject("recog");
        String content = recog.getString("content");
        return R.success(content);
    }

    @Override
    public R<List<WordScoreVO>> speechAssessment(SpeechAssessmentDTO speechAssessmentDTO) throws Exception {
        String hostUrl = "https://ise-api.xfyun.cn/v2/open-ise";
        String category = speechAssessmentDTO.getCategory();
        System.out.println("当前题目类型为=》" + speechAssessmentDTO.getCategory());
        String text = speechAssessmentDTO.getText();
        // 根据category为text加上前缀
        if (Objects.equals(category, "read_word")) {
            text = "[word]\n" + text;
        }
        if (Objects.equals(category, "read_sentence")) {
            text = "[content]\n" + text;
        }
        if (Objects.equals(category, "read_chapter")) {
            text = "[content]\n" + text;
        }
        String authUrl = AuthUtil.getAuthUrl(hostUrl,apiKey,apiSecret,"GET").replace("http://", "ws://").replace("https://", "wss://");//将url中的 schema http://和https://分别替换为ws:// 和 wss://;
        OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request.Builder().url(authUrl).build();
        String params = buildSpeechAssessmentRequest(text,category);
        StringBuilder answer = new StringBuilder();
        SpeechAssessmentListener listener = new SpeechAssessmentListener(speechAssessmentDTO.getFile(),params,answer);
        client.newWebSocket(request, listener);
        while ( !listener.getWsCloseFlag() ) {
            Thread.sleep(100);
        }
        System.out.println(answer.toString());
        List<WordScoreVO> wordScoreVOS = new ArrayList<>();
        // 加载xml
        Document document = DocumentHelper.parseText(answer.toString());
        // 获取根节点
        Element root = document.getRootElement();
        if ( Objects.equals(category, "read_word")) {
            Element readWord = root.element("read_word").element("rec_paper").element("read_word");
            wordScoreVOS.add(parseElement(readWord,category));
        }
        if ( Objects.equals(category, "read_sentence")) {
            Element readChapter = root.element("read_sentence").element("rec_paper").element("read_chapter");
            wordScoreVOS.add(parseElement(readChapter,category));
            for (Object element : readChapter.element("sentence").elements("word")) {
                wordScoreVOS.add(parseElement((Element) element,category));
            }
        }
        if ( Objects.equals(category, "read_chapter")) {
            Element readChapter = root.element("read_chapter").element("rec_paper").element("read_chapter");
            wordScoreVOS.add(parseElement(readChapter,category));
            for (Object element : readChapter.element("sentence").elements("word")) {
                wordScoreVOS.add(parseElement((Element) element,category));
            }
        }
        return R.success(wordScoreVOS);
    }


    @Override
    public R<String> getRandomSpeechText(String category) {
        String[] words = {"apple", "banana", "cherry", "date", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon"};
        String[] sentences = {"I love apples.", "Bananas are my favorite fruit.", "Cherries are so delicious.", "The date tree is a beautiful plant.", "Elderberries are a rare treat.", "Fig trees are known for their fragrant fruit.", "Grapes are a popular fruit.", "Honeydew melons are a summer favorite.", "Kiwis are a unique"};
        String[] paragraphs = {"The apple tree is a popular tree in the world. It is known for its beautiful fruit.", "Bananas are a popular fruit that grows in warm climates. They are known for their sweet and yellow color.", "Cherries are a popular fruit that grows in cool climates. They are known for their delicious taste.", "The date tree is a beautiful plant that grows in warm"};
        if (Objects.equals(category, "read_word")) {
            // 随机从words里挑一个返回
            return R.success(words[new Random().nextInt(words.length)]);
        }
        if ( Objects.equals(category, "read_sentence") ) {
            // 随机从sentences里挑一个返回
            return R.success(sentences[new Random().nextInt(sentences.length)]);
        }
        if ( Objects.equals(category, "read_chapter") ) {
            // 随机从paragraphs里挑一个返回
            return R.success(paragraphs[new Random().nextInt(paragraphs.length)]);
        }
        return null;
    }

    @Override
    public R<List<PhotoCalculateVO>> photoCalculate(String uid) throws Exception {
        String hostUrl = "https://rest-api.xfyun.cn/v2/itr";
        System.out.println(uid);
        String base64Url = redisService.get(AiPrefix.BASE64URL,uid);
        System.out.println(base64Url);
        String params = buildPhotoCalculateRequest(base64Url);
        Map<String, String> header = buildHttpHeader(hostUrl,params,apiKey,apiSecret);
        Map<String, Object> resultMap = HttpUtil.doPostByStringBody(hostUrl,header, params);
        System.out.println("返回结果为：" + resultMap);
        List<PhotoCalculateVO> list = new ArrayList<>();
        if (resultMap != null) {
            String resp = resultMap.get("body").toString();
            // 转化为JSON字符串
            JSONObject responseJSONObject = JSONObject.parseObject(resp);
            System.out.println("接口真正调用结果：" + resp);
            JSONObject data = responseJSONObject.getJSONObject("data");
            JSONObject ITRResult = data.getJSONObject("ITRResult");
            JSONObject multiLineInfo = ITRResult.getJSONObject("multi_line_info");
            JSONArray impLineInfoArray = multiLineInfo.getJSONArray("imp_line_info");
            for (int i = 0; i < impLineInfoArray.size(); i++) {
                PhotoCalculateVO photoCalculateVO = new PhotoCalculateVO();
                JSONObject jsonObject = impLineInfoArray.getJSONObject(i);
                JSONObject impLineRect = jsonObject.getJSONObject("imp_line_rect");
                photoCalculateVO.setLeftX(impLineRect.getInteger("left_up_point_x"));
                photoCalculateVO.setLeftY(impLineRect.getInteger("left_up_point_y"));
                photoCalculateVO.setRightX(impLineRect.getInteger("right_down_point_x"));
                photoCalculateVO.setRightY(impLineRect.getInteger("right_down_point_y"));
                photoCalculateVO.setIsRight(jsonObject.getInteger("total_score"));
                list.add(photoCalculateVO);
            }
        }
        System.out.println("拍照速算结果=========");
        for (PhotoCalculateVO photoCalculateVO : list) {
            System.out.println(photoCalculateVO.toString());
        }
        return R.success(list);
    }

    @Override
    public R<List<Integer>> getToolFrequency(HttpServletRequest request) {
        // 获取用户id
        Long userId = jwtUtil.parseJwt(request.getHeader("token")).getId();
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();
        LambdaQueryWrapper<ToolFrequency> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ToolFrequency::getUserId, userId);
        queryWrapper.eq(ToolFrequency::getUseDate, currentDate);
        ToolFrequency toolFrequency = toolFrequencyMapper.selectOne(queryWrapper);
        List<Integer> list = new ArrayList<>();
        if ( toolFrequency == null ) {
            // 没有查到 添加一条信息
            toolFrequency = new ToolFrequency();
            toolFrequency.setUserId(userId);
            toolFrequency.setUseDate(currentDate);
            toolFrequency.setFrequency("0,0,0,0,0,0,0,0,0");
            toolFrequency.setUseDate(LocalDate.now());
            toolFrequencyMapper.insert(toolFrequency);
            // 没有数据 9种功能调用频率默认为0
            for ( int i = 1 ; i <= 9 ; i ++ ) {
                list.add(0);
            }
            return R.success(list);
        }
        // 查到
        String frequency = toolFrequency.getFrequency();
        String[] frequencyArray = frequency.split(",");
        for (String s : frequencyArray) {
            list.add(Integer.parseInt(s));
        }
        return R.success(list);
    }

    @Override
    public R updateToolFrequency(Integer idx, HttpServletRequest request) {
        Long userId = jwtUtil.parseJwt(request.getHeader("token")).getId();
        LocalDate currentDate = LocalDate.now();
        LambdaQueryWrapper<ToolFrequency> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ToolFrequency::getUserId, userId);
        queryWrapper.eq(ToolFrequency::getUseDate, currentDate);
        ToolFrequency toolFrequency = toolFrequencyMapper.selectOne(queryWrapper);
        if ( toolFrequency != null ) {
            String frequency = toolFrequency.getFrequency();
            String[] frequencyArray = frequency.split(",");
            frequencyArray[idx - 1] = String.valueOf(Integer.parseInt(frequencyArray[idx - 1]) + 1);
            toolFrequency.setFrequency(String.join(",", frequencyArray));
            toolFrequencyMapper.updateById(toolFrequency);
        }else {
            return R.error("信息不存在！");
        }
        return R.success("更新成功");
    }

    @Override
    public MyWebSocketClient realTimeASR(String source,SseEmitter sseEmitter) throws Exception {
        sseEmitter.send("字幕生成中，请稍等...");
        final int CHUNCKED_SIZE = 1280;
        String HOST = "rtasr.xfyun.cn/v1/ws";
        String BASE_URL = "ws://" + HOST;
        String ORIGIN = "http://" + HOST;
        URI url = new URI(BASE_URL + getHandShakeParams(appId, realTimeSecretKey));

        DraftWithOrigin draft = new DraftWithOrigin(ORIGIN);
        CountDownLatch handshakeSuccess = new CountDownLatch(1);
        CountDownLatch connectClose = new CountDownLatch(1);
        MyWebSocketClient client = new MyWebSocketClient(url, draft, handshakeSuccess, connectClose,sseEmitter);
        client.connect();

        // 处理OSS链接 获取去掉前面桶 + 地域的链接
        StringBuilder sourceBuilder = new StringBuilder();
        String[] split = source.split("/");
        for ( int i = 3; i < split.length; i++) {
            sourceBuilder.append(split[i]);
            if ( i != split.length - 1 ) {
                sourceBuilder.append("/");
            }
        }
        System.out.println(sourceBuilder);
        String fileName = split[split.length - 1];
        String localPath = "D:\\Project\\Java\\iflytek-education\\iflytek_education_ui\\src\\assets\\mp4\\" + fileName;
        File file = new File(localPath);
        if ( !file.exists() ) {
            ossService.download(sourceBuilder.toString(), localPath);
        }
        while (!file.exists()) {
            Thread.sleep(100);
        }
        String tempFileLink = "D:\\Project\\Java\\iflytek-education\\iflytek_education_ui\\src\\assets\\pcm";
        String mp4Pcm = Mp4ExtractAudioUtils.getMp4Pcm(localPath, tempFileLink);
        System.out.println(client.getConnectionLostTimeout());
        while (!client.getReadyState().equals(org.java_websocket.WebSocket.READYSTATE.OPEN)) {
            System.out.println("连接中");
            Thread.sleep(1000);
            // 太久没有反应 关闭连接
            if (System.currentTimeMillis() - client.getConnectionLostTimeout() > 10000) {
                return null;
            }
        }
        // 等待握手成功
        handshakeSuccess.await();
        System.out.println(" 开始发送音频数据");
        // 发送音频
        byte[] bytes = new byte[CHUNCKED_SIZE];
        try (RandomAccessFile raf = new RandomAccessFile(mp4Pcm, "r")) {
            int len = -1;
            long lastTs = 0;
            while ((len = raf.read(bytes)) != -1 && !realTimeASRFlag) {
                if (len < CHUNCKED_SIZE) {
                    send(client, bytes = Arrays.copyOfRange(bytes, 0, len));
                    break;
                }

                long curTs = System.currentTimeMillis();
                if (lastTs == 0) {
                    lastTs = System.currentTimeMillis();
                } else {
                    long s = curTs - lastTs;
                    if (s < 40) {
                        System.out.println("error time interval: " + s + " ms");
                    }
                }
                send(client, bytes);
                // 每隔40毫秒发送一次数据
                Thread.sleep(40);
            }
            if ( !realTimeASRFlag) {
                // 发送结束标识
                send(client, "{\"end\": true}".getBytes());
                System.out.println("发送结束标识完成");
            }else {
                // 如果是手动停止实时语音识别后 还要恢复便于下次使用
                realTimeASRFlag = false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 等待连接关闭
        connectClose.await();
        return client;
    }

    @Override
    public R<Integer> faceCompare(FormData formData,HttpServletRequest request) throws Exception {
        MultipartFile uploadFile = formData.getFile();
        // 获取文件后缀
        String examUserPhotoSuffix = Objects.requireNonNull(uploadFile.getOriginalFilename()).substring(uploadFile.getOriginalFilename().lastIndexOf(".")).substring(1).toLowerCase();
        // 正在考试的学生的照片base64链接
        String examUserPhotoBase64Link = Base64.getEncoder().encodeToString(uploadFile.getBytes());
        Long userId = jwtUtil.parseJwt(request.getHeader("token")).getId();
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getId,userId);
        User user = userMapper.selectOne(userLambdaQueryWrapper);
        String photoLink = user.getPhotoLink();
        if ( photoLink == null || photoLink.isEmpty()) {
            return R.error("证件照不存在!");
        }
        // 处理OSS链接 获取去掉前面桶 + 地域的链接
        StringBuilder sourceBuilder = new StringBuilder();
        String[] split = photoLink.split("/");
        for ( int i = 3; i < split.length; i++) {
            sourceBuilder.append(split[i]);
            if ( i != split.length - 1 ) {
                sourceBuilder.append("/");
            }
        }
        String fileName = split[split.length - 1];
        String localPath = "D:\\Project\\Java\\iflytek-education\\iflytek_education_ui\\src\\assets\\photo\\" + fileName;
        File file = new File(localPath);
        if ( !file.exists() ) {
            ossService.download(sourceBuilder.toString(), localPath);
        }
        while (!file.exists()) {
            Thread.sleep(100);
        }
        String[] existUserPhotoFileName = fileName.split("\\.");
        String existUserPhotoSuffix = existUserPhotoFileName[existUserPhotoFileName.length - 1].toLowerCase();
        String existUserPhotoBase64Link = Base64.getEncoder().encodeToString(readImage(localPath));
        String hostUrl = "https://api.xf-yun.com/v1/private/s67c9c78c";
        String requestUrl = getAuthUrl(hostUrl,apiKey,apiSecret,"POST");
        String xParams = getXParam(examUserPhotoBase64Link, examUserPhotoSuffix, existUserPhotoBase64Link, existUserPhotoSuffix);
        System.out.println(examUserPhotoSuffix);
        System.out.println(existUserPhotoSuffix);
        Map<String,String> headers = new HashMap<>();
        headers.put("Content-type", "application/json");
        String result = HttpUtil.doPostJsonMapParams(requestUrl, headers,xParams);
        if (result != null) {
            System.out.println("人脸识别接口调用结果：" + result);
            FaceCompareResponse faceCompareResponse = gson.fromJson(result, FaceCompareResponse.class);
            String text = faceCompareResponse.getPayload().getFace_compare_result().getText();
            String textJson = new String(Base64Decoder.decode(text), StandardCharsets.UTF_8);
            System.out.println("TextJSON");
            System.out.println(textJson);
            if ( gson.fromJson(textJson, Text.class).getScore() > 0.67 ) {
                return R.success(1);
            }else {
                return R.success(0);
            }
        } else {
            return null;
        }
    }

    @Override
    public SparkChatListener analysisCode(StringBuilder question, StringBuilder answer) throws Exception {
        String hostUrl = "https://spark-api.xf-yun.com/v3.5/chat";
        // 构建鉴权url
        String authUrl = getAuthUrl(hostUrl,apiKey,apiSecret,"GET");
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 构建聊天请求
        JSONObject chatRequest = buildAnalysisCodeRequest(question.toString());
        // 构建websocket请求
        Request request = new Request.Builder().url(authUrl).build();
        SparkChatListener sparkChatListener = new SparkChatListener(answer,new ArrayList<>());
        // 发起websocket请求
        WebSocket webSocket = client.newWebSocket(request, sparkChatListener);
        if (chatRequest != null) {
            webSocket.send(chatRequest.toJSONString());
        }
        return sparkChatListener;
    }

    @Override
    public R stopRealTimeASR() {
        realTimeASRFlag = true;
        return R.success("停止成功");
    }

    private JSONObject buildAnalysisCodeRequest(String question) {
        try {
            JSONObject requestJson = new JSONObject();

            JSONObject header = new JSONObject();
            header.put("app_id", appId);
            header.put("uid", UUID.randomUUID().toString().substring(0, 10));

            JSONObject parameter = new JSONObject();
            JSONObject chat = new JSONObject();

            chat.put("domain", "generalv2");
            chat.put("temperature", 0.5);
            chat.put("max_tokens", 4096);
            parameter.put("chat", chat);

            JSONObject payload = new JSONObject();
            JSONObject message = new JSONObject();
            JSONArray text = new JSONArray();

            RoleContent roleContent = new RoleContent();
            roleContent.setRole("user");
            roleContent.setContent(question);
            roleContent.setContent_type("text");
            text.add(JSON.toJSON(roleContent));

            message.put("text", text);
            payload.put("message", message);

            requestJson.put("header", header);
            requestJson.put("parameter", parameter);
            requestJson.put("payload", payload);
            System.err.println(requestJson);
            return requestJson;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // 获取人脸比对参数
    public String getXParam(String imageBase641, String imageEncoding1, String imageBase642, String imageEncoding2) {
        JsonObject jso = new JsonObject();

        /** header **/
        JsonObject header = new JsonObject();
        header.addProperty("app_id", appId);
        header.addProperty("status", 3);

        jso.add("header", header);

        /** parameter **/
        JsonObject parameter = new JsonObject();
        JsonObject service = new JsonObject();
        service.addProperty("service_kind", "face_compare");

        JsonObject faceCompareResult = new JsonObject();
        faceCompareResult.addProperty("encoding", "utf8");
        faceCompareResult.addProperty("format", "json");
        faceCompareResult.addProperty("compress", "raw");
        service.add("face_compare_result", faceCompareResult);
        parameter.add("s67c9c78c", service);
        jso.add("parameter", parameter);

        /** payload **/
        JsonObject payload = new JsonObject();
        JsonObject inputImage1 = new JsonObject();
        inputImage1.addProperty("encoding", imageEncoding1);
        inputImage1.addProperty("image", imageBase641);
        payload.add("input1", inputImage1);

        JsonObject inputImage2 = new JsonObject();
        inputImage2.addProperty("encoding", imageEncoding2);
        inputImage2.addProperty("image", imageBase642);
        payload.add("input2", inputImage2);
        System.out.println(jso.toString());
        jso.add("payload", payload);
        return jso.toString();
    }

    //读取image
    private byte[] readImage(String imagePath) throws IOException {
        InputStream is = new FileInputStream(imagePath);
        byte[] imageByteArray1 = FileUtil.read(imagePath);
        //return is.readAllBytes();
        return imageByteArray1;
    }

    private WordScoreVO parseElement(Element element,String type) {
        WordScoreVO wordScoreVO = new WordScoreVO();
        wordScoreVO.setText(element.attribute("content").getValue());
        if (!Objects.equals(element.getName(), "word")) {
            wordScoreVO.setAccuracyScore(Double.parseDouble(element.attribute("accuracy_score").getValue()));
            // 单词读写暂时没有流畅度评分
            if ( Objects.equals(type, "read_word")) {
                wordScoreVO.setFluencyScore(-1);
                wordScoreVO.setIntegrityScore(-1);
            }else {
                wordScoreVO.setFluencyScore(Double.parseDouble(element.attribute("fluency_score").getValue()));
                wordScoreVO.setIntegrityScore(Double.parseDouble(element.attribute("integrity_score").getValue()));
            }
            wordScoreVO.setTotalScore(Double.parseDouble(element.attribute("standard_score").getValue()));
            wordScoreVO.setTotalScore(Double.parseDouble(element.attribute("total_score").getValue()));
            String isRejected = element.attribute("is_rejected").getValue();
            System.out.println("isReject" + isRejected);
            wordScoreVO.setIsRejected(Objects.equals(isRejected, "true") ? 1 : 0);
            String rejectType = element.attribute("reject_type").getValue();
            if ( rejectType.equals("0") ) {
                wordScoreVO.setRejectedType("正常");
            }
            if ( rejectType.equals("16") ) {
                wordScoreVO.setRejectedType("漏读");
            }
            if ( rejectType.equals("32") ) {
                wordScoreVO.setRejectedType("增读");
            }
            if ( rejectType.equals("64") ) {
                wordScoreVO.setRejectedType("回读");
            }
            if ( rejectType.equals("128") ) {
                wordScoreVO.setRejectedType("替换");
            }
        }else {
            wordScoreVO.setText(element.attribute("content").getValue());
            wordScoreVO.setAccuracyScore(-1);
            wordScoreVO.setStandardScore(-1);
            wordScoreVO.setFluencyScore(-1);
            wordScoreVO.setIntegrityScore(-1);
            if ( !Objects.isNull(element.attribute("total_score"))) {
                wordScoreVO.setTotalScore(Double.parseDouble(element.attribute("total_score").getValue()));
            }else {
                wordScoreVO.setTotalScore(-1);
                return wordScoreVO;
            }
            String rejectType = element.attribute("dp_message").getValue();
            if ( rejectType.equals("0") ) {
                wordScoreVO.setIsRejected(0);
                wordScoreVO.setRejectedType("正常");
            }else {
                wordScoreVO.setIsRejected(1);
                if ( rejectType.equals("16") ) {
                    wordScoreVO.setRejectedType("漏读");
                }
                if ( rejectType.equals("32") ) {
                    wordScoreVO.setRejectedType("增读");
                }
                if ( rejectType.equals("64") ) {
                    wordScoreVO.setRejectedType("回读");
                }
                if ( rejectType.equals("128") ) {
                    wordScoreVO.setRejectedType("替换");
                }
            }
        }
        return wordScoreVO;
    }

    private static void send(WebSocketClient client, byte[] bytes) {
        if (client.isClosed()) {
            throw new RuntimeException("client connect closed!");
        }
        client.send(bytes);
    }

    private String buildPhotoCalculateRequest(String base64Url) {
        JsonObject body = new JsonObject();
        JsonObject business = new JsonObject();
        JsonObject common = new JsonObject();
        JsonObject data = new JsonObject();
        //填充common
        common.addProperty("app_id", appId);
        //填充business
        business.addProperty("ent", "math-arith");
        business.addProperty("aue", "raw");
        //填充data
        data.addProperty("image", base64Url);
        //填充body
        body.add("common", common);
        body.add("business", business);
        body.add("data", data);

        return body.toString();
    }

    private String buildSpeechAssessmentRequest(String text,String category) {
        ParamBuilder p = new ParamBuilder();
        p.add("common", new ParamBuilder()
                        .add("app_id", appId)
                )
                .add("business", new ParamBuilder()
                        .add("category", category)
                        .add("rstcd", "utf8")

                        //群体：adult成人 、youth（中学，效果与设置pupil参数一致）、pupil小学
                        //仅中文字、词、句题型支持
                        //.add("group", "pupil")

                        //打分门限值：hard、common、easy
                        //仅英文引擎支持
                        //.add("check_type","common")

                        //学段：junior(1,2年级) middle(3,4年级) senior(5,6年级)
                        //仅中文题型：中小学的句子、篇章题型支持
                        //.add("grade","junior")

                        //extra_ability生效条件：ise_unite=1,rst=entirety
                        //.add("ise_unite","1")
                        //.add("rst","entirety")
                        /*1.全维度(准确度分、流畅度分、完整度打分) ,extra_ability值为multi_dimension
                          2.支持因素错误信息显示(声韵、调型是否正确),extra_ability值为syll_phone_err_msg
                          3.单词基频信息显示（基频开始值、结束值）,extra_ability值为pitch ，仅适用于单词和句子题型
                          4.(字词句篇均适用,如选多个能力，用分号;隔开如:syll_phone_err_msg;pitch;multi_dimension)*/
                        //.add("extra_ability","multi_dimension")

                        //试卷部分添加拼音,限制条件：添加拼音的汉字个数不超过整个试卷中汉字个数的三分之一。
                        //jin1|tian1|天气怎么样支持

                        //分制转换，rst=entirety是默认值，请根据文档推荐选择使用百分制


                        .add("sub","ise")
                        .add("ent","en_vip") // 评测语言选择英文
                        .add("tte", "utf-8")
                        .add("cmd", "ssb")
                        .add("auf", "audio/L16;rate=16000")
                        .add("aue", "lame") // 上传文件为mp3 选择使用lame接收
                        //评测文本(new String(new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF })+text)
                        .add("text",'\uFEFF'+text)//Base64.getEncoder().encodeToString(text.getBytes())
                ).add("data", new ParamBuilder()
                        .add("status", 0)
                        .add("data", ""));
        //System.err.println(p.toString());
        return p.toString();
    }

    private String buildFormulaRecognitionRequest(String base64Url) {
        JsonObject body = new JsonObject();
        JsonObject business = new JsonObject();
        JsonObject common = new JsonObject();
        JsonObject data = new JsonObject();
        //填充common
        common.addProperty("app_id", appId);
        //填充business
        business.addProperty("ent", "teach-photo-print");
        business.addProperty("aue", "raw");
        //填充data
        data.addProperty("image", base64Url);
        //填充body
        body.add("common", common);
        body.add("business", business);
        body.add("data", data);

        return body.toString();
    }

    private String buildPictureDocumentRequest(String base64Link, String type,String encoding) {
        //请求json串
        String requestJson;
        requestJson = "{\n" +
                "  \"header\": {\n" +
                "    \"app_id\": \"" + appId + "\",\n" +
                "    \"status\":2\n" +
                "  },\n" +
                "  \"parameter\": {\n" +
                "    \"s15282f39\": {\n" +
                "      \"category\": \"ch_en_public_cloud\",\n" +
                "      \"result\": {\n" +
                "        \"encoding\": \"utf8\",\n" +
                "        \"compress\": \"raw\",\n" +
                "        \"format\": \"plain\"\n" +
                "      }\n" +
                "    },\n" +
                "    \"s5eac762f\": {\n" +
                "      \"result_type\": \"" + type + "\",\n" +
                "      \"result\": {\n" +
                "        \"encoding\": \"utf8\",\n" +
                "        \"compress\": \"raw\",\n" +
                "        \"format\": \"plain\"\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"payload\": {\n" +
                "    \"test\": {\n" +
                "      \"encoding\": \"" + encoding + "\",\n" +
                "      \"image\": \"" + base64Link + "\",\n" +
                "      \"status\": 3\n" +
                "    }\n" +
                "  }\n" +
                "}";
        return requestJson;
    }

    // 发送请求方法
    private String doRequest(String hostUrl,String params,String apiKey,String apiSecret) throws Exception {
        URL realUrl = new URL(AuthUtil.getAuthUrl(hostUrl,apiKey,apiSecret,"POST"));
        URLConnection connection = realUrl.openConnection();
        HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
        httpURLConnection.setDoInput(true);
        httpURLConnection.setDoOutput(true);
        httpURLConnection.setRequestMethod("POST");
        httpURLConnection.setRequestProperty("Content-type","application/json");

        OutputStream out = httpURLConnection.getOutputStream();
        // System.out.println("params=>"+params);
        out.write(params.getBytes(StandardCharsets.UTF_8));
        out.flush();
        InputStream is;
        try{
            is = httpURLConnection.getInputStream();
        }catch (Exception e){
            is = httpURLConnection.getErrorStream();
            throw new Exception("make request error:"+"code is "+httpURLConnection.getResponseMessage()+readAllBytes(is));
        }
        return readAllBytes(is);
    }

    // 请求参数json拼接
    private String getCorrectErrorRequest(String text) {
        return "{\n" +
                "  \"header\": {\n" +
                "    \"app_id\": \"" + appId + "\",\n" +
                //"    \"uid\": \"XXXXX\",\n" +
                "    \"status\": 3\n" +
                "  },\n" +
                "  \"parameter\": {\n" +
                "    \"s9a87e3ec\": {\n" +
                //"    \"res_id\": \"XXXXX\",\n" +
                "      \"result\": {\n" +
                "        \"encoding\": \"utf8\",\n" +
                "        \"compress\": \"raw\",\n" +
                "        \"format\": \"json\"\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"payload\": {\n" +
                "    \"input\": {\n" +
                "      \"encoding\": \"utf8\",\n" +
                "      \"compress\": \"raw\",\n" +
                "      \"format\": \"plain\",\n" +
                "      \"status\": 3,\n" +
                "      \"text\": \"" +  Base64.getEncoder().encodeToString(text.getBytes()) + "\"\n" +
                "    }\n" +
                "  }\n" +
                "}";
    }

    // 组装机器翻译请求参数
    private String buildMachineTranslateParams(String appId,String resId,String text,String from,String to) {
        String param = "{"+
                "    \"header\": {"+
                "        \"app_id\": \""+appId+"\","+
                "        \"status\": 3,"+
                "        \"res_id\": \""+resId+"\""+
                "    },"+
                "    \"parameter\": {"+
                "        \"its\": {"+
                "            \"from\": \""+from+"\","+
                "            \"to\": \""+to+"\","+
                "            \"result\": {}"+
                "        }"+
                "    },"+
                "    \"payload\": {"+
                "        \"input_data\": {"+
                "            \"encoding\": \"utf8\","+
                "            \"status\": 3,"+
                "            \"text\": \""+ Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8))+"\""+
                "        }"+
                "    }"+
                "}";
        return param;
    }

    // 组装文本重写请求参数
    private String buildTextRewriteRequest(String appId,String level,String text) {
        String param = "{"+
                "    \"header\": {"+
                "        \"app_id\": \""+appId+"\","+
                "        \"status\": 3"+
                "    },"+
                "    \"parameter\": {"+
                "        \"se3acbe7f\": {"+
                "            \"level\": \""+level+"\","+
                "            \"result\": {"+
                "                \"encoding\": \"utf8\","+
                "                \"compress\": \"raw\","+
                "                \"format\": \"json\""+
                "            }"+
                "        }"+
                "    },"+
                "    \"payload\": {"+
                "        \"input1\": {"+
                "            \"encoding\": \"utf8\","+
                "            \"compress\": \"raw\","+
                "            \"format\": \"plain\","+
                "            \"status\": 3,"+
                "            \"text\": \""+Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8))+"\""+
                "        }"+
                "    }"+
                "}";
        return param;
    }

    // 构造图片转文字的请求参数
    private String  buildCharacterRecognitionRequest(String base64Link) throws IOException {
        String param = "{"+
                "    \"header\": {"+
                "        \"app_id\": \""+appId+"\","+
                "        \"status\": 3"+
                "    },"+
                "    \"parameter\": {"+
                "        \"sf8e6aca1\": {"+
                "            \"category\": \"ch_en_public_cloud\","+
                "            \"result\": {"+
                "                \"encoding\": \"utf8\","+
                "                \"compress\": \"raw\","+
                "                \"format\": \"json\""+
                "            }"+
                "        }"+
                "    },"+
                "    \"payload\": {"+
                "        \"sf8e6aca1_data_1\": {"+
                "            \"encoding\": \"jpg\","+
                "            \"status\": " + 3 + "," +
                "            \"image\": \""+base64Link+"\""+
                "        }"+
                "    }"+
                "}";
        return param;
    }

    // 读取流数据
    private String readAllBytes(InputStream is) throws IOException {
        byte[] b = new byte[1024];
        StringBuilder sb = new StringBuilder();
        int len = 0;
        while ((len = is.read(b)) != -1){
            sb.append(new String(b, 0, len, "utf-8"));
        }
        return sb.toString();
    }
}
